﻿/****************************************************************************
** file: CameraImage.h
** brief: 提供了相机数据解析为 QImage 功能的接口。这个类及派生类实现的功能很多。
** 包括解析 Bayer 格式,解析 YUV 格式，图像的水平、竖直翻转等。
** Copyright (C) LiYuan
** Author: LiYuan
** E-Mail: 18069211#qq(.)com
** Version 2.0.1
** Last modified: 2019.01.11
** Modified By: LiYuan
****************************************************************************/


#ifndef CAMERAIMAGE_H
#define CAMERAIMAGE_H
#include <QStack>
#include <QObject>
#include <QImage>
#include <QSize>
#include <QMutex>

//#include <QVideoFrame>
//#include <QAbstractVideoSurface>

#include "VideoFrame.h"

namespace Qly
{
/**
 * @brief The CameraImage class 这个类是纯虚类，提供了相机原始图像数据到 QImage 类的转换功能。
 */
class CAMERA_EXPORT CameraImage : public QObject
{
    Q_OBJECT
public:
    enum FlipMode{ NONE = 0, HORIZONTAL_FLIP = 1, VERTICAL_FLIP = 2, BOTH = 3};
    explicit CameraImage(QObject *parent = 0);
    AVPixelFormat bayerPattern() const {return m_inputPixelFormat;}

    virtual bool isModified() {return m_isModified;}
    int imageCount() {return m_imageCount;}
    QImage getImage(); // 获取图像
    QImage &imageRef();
    /**
    * @brief 加载文件中的图像
    * @param fileName 带路径的完整文件名
    * @return true 表示加载成功，false 表示加载失败
    */
    bool loadImage(const QString &fileName);

    /**
     * @brief saveImage 将图像保存到文件中
     * @param fileName 带路径的完整文件名
     * @return true 表示保存成功
     */
    bool saveImage(const QString &fileName);

    bool undoStatus() {return !m_undoStack.isEmpty();}
signals:
    /**
     * @brief flipModeChanged 图像翻转模式改变时发送这个信号
     * @param mode
     */
    void flipModeChanged(CameraImage::FlipMode mode);
    /**
     * @brief bayerPatternChanged Color Filter 起始图案更改时发送这个信号
     * @param pattern
     */
    void bayerPatternChanged(AVPixelFormat pattern);

    /**
     * @brief imageChanged 当图像发生变化时发送这个信号
     * @param image
     */
    void imageChanged(QImage &image);
    void undoStatusChanged(bool valid);
public slots:
    void push(); // 将当前图像保存到 undo 列表里
    bool undo(); // 恢复上一幅图像
    virtual void setImage(const QImage &image) = 0;
    void setVideoFrame(VideoFrame frame);
    void setImageData(const uint8_t *imgBuf, int stride, QSize size, AVPixelFormat format);

    virtual void fillImageTestPattern() = 0;
    /**
     * @brief setFlipMode 设置图像翻转的模式。每次更新图像时，可以对图像进行左右翻转或上下翻转，也可同时翻转。
     * @param flipMode
     */
    void setFlipMode(CameraImage::FlipMode flipMode);
    /**
     * @brief setHorizontalFlip 设置图像是否左右翻转
     * @param on true 表示开启图像左右翻转
     */
    void setHorizontalFlip(bool on);
    /**
     * @brief setVerticalFlip 设置图像是否上下翻转
     * @param on true 表示开启图像上下翻转
     */
    void setVerticalFlip(bool on);
    /**
     * @brief setBayerPattern 制定 Color Filter 的起始图案。
     *                        对于大多数相机，都不需要设置这个参数。因为相机传过来的数据就包含了 Bayer 格式类型。
     *                        但是 映美精 相机传过来的数据没有 Bayer 类型。只是简单的当做 Gray8 传过来。
     *                        所以这个函数可以说是针对映美精相机打的一个补丁。
     *                        对于其他相机，pattern 传入 AV_PIX_FMT_GRAY8。
     *                        这样会关闭将 GRAY8 解释成 Bayer 格式。
     * @param pattern  Bayer 类型，可以为以下四种之一。其他类型则关闭 Bayer 模式。
     *                 AV_PIX_FMT_BAYER_BGGR8
     *                 AV_PIX_FMT_BAYER_GBRG8
     *                 AV_PIX_FMT_BAYER_GRBG8
     *                 AV_PIX_FMT_BAYER_RGGB8
     */
    void setBayerPattern(AVPixelFormat pattern);

    void setGrayMode(bool on) {m_isGrayMode = on;}
protected:
    void preOperate(); // 更新图像前做的工作
    void postOperate(); // 更新图像后做的工作
    virtual void preImageChangedHook() {}
    virtual void postImageChangedHook() {}

    int m_flipMode; // 这里故意设为int类型，不用FlipMode类型
    AVPixelFormat m_inputPixelFormat = AV_PIX_FMT_GRAY8;
    QImage m_image;
    int m_imageCount;
    QMutex m_imageMutex; ///< m_image 会被多个线程访问，需用 mutex 保护
    bool m_isModified = false;
    bool m_isGrayMode = false;

    QStack<QImage> m_undoStack;
private:
    virtual void setRGBImage(const uint8_t *imgBuf, int stride, AVPixelFormat format, QSize size) = 0;
    virtual void setYUV422Image(const uint8_t *imgBuf, AVPixelFormat format, QSize size) = 0;
    virtual void setBayerImage(const uint8_t *imgBuf, int stride, QSize size) = 0;
    virtual void setGray8Image(const uint8_t *imgBuf, int stride, QSize size)  = 0;
    virtual void flipImage(QImage &image, FlipMode flip_mode) = 0;
    virtual void toGrayImage(QImage &image) = 0;
};

} //namespac Qly
#endif // CAMERAIMAGE_H
